#include "hexcodes.h"
#include "int18xxx.h"

#include "time.h"
#include "rtc.h"
#include "debounce.h"
#include "pwm2.h"
#include "io.h"
#include "eeprom.h"
#include "epwm.h"
#include "param.h"
#include "irda.h"

#define  FREQ  64000000
#define  FOSC  16000000

// CONFIG Registers

#define CONFIG1H config[1]
#define CONFIG2L config[2]
#define CONFIG2H config[3]
#define CONFIG3H config[5]
#define CONFIG4L config[6]
#define CONFIG5L config[8]
#define CONFIG5H config[9]
#define CONFIG6L config[10]
#define CONFIG6H config[11]
#define CONFIG7L config[12]
#define CONFIG7H config[13]

// Comments state which options are not left at defaut settings
#pragma CONFIG1H = 0b00111000    // Internal OSC, PLL enabled Freq=64MHz
#pragma CONFIG2L = 0b00011111    // default
#pragma CONFIG2H = 0b00000110    // WDT controlled in WDTCON register, 1:8 postscale (32mS)
#pragma CONFIG3H = 0b00000000    // MCLR internal, wait for HFINTOSC to stabilize
#pragma CONFIG4L = 0b00000001    // Single supply ICSP disabled
#pragma CONFIG5L = 0b00000011    // default
#pragma CONFIG5H = 0b11000000    // default
#pragma CONFIG6L = 0b00000011    // default
#pragma CONFIG6H = 0b11100000    // default
#pragma CONFIG7L = 0b00000011    // default
#pragma CONFIG7H = 0b01000000    // default



/* Pin   Pin   Pragma
   SOT   DIP   Port  Dir   Name
   1     1     Vdd         Power
   2     2     RA5   Out   DIR
   3     3     AN3   Ana   T_MON
   4     4     RA3   In    SEL
   5     5     RC5   Out   HB_PWM (P1A)
   6     6     RC4   Out   SP_PWM (P1B)
   7     7     RC3   Out   HS_PWM (P1C)
   8     8     RC6   In    Spare_A
   9     9     RC7   In    Spare_B
   10    10    RB7   In    RX
   11    11    RB6   Out   CLK
   12    12    RB5   Out   TX
   13    13    RB4   Out   EN
   14    14    AN6   Ana   HB_MON
   15    15    AN5   Ana   SP_MON
   16    16    AN4   Ana   HS_MON
   17    17    AN2   Ana   V_MON
   18    18    RA1   In    LB
   19    19    RA0   In    HB
   20    20    Vss         Ground
*/


/*
	Resource allocation:

	AtoD: 	Pins AN2, AN3, AN4, AN5, AN6
	TimerO:	IrDA clock
	Timer1:	OS Tick & Real Time Clock
	Timer2:	EPWM
	Timer3:	PWM2



*/

#define HS_pin PORTC.3
#define SP_pin PORTC.4
#define HB_pin PORTC.5

#define HS_tris TRISC.3
#define SP_tris TRISC.4
#define HB_tris TRISC.5

#define PWM2_PIN  HS_pin

#define  V_MON_CHAN  2
#define  T_MON_CHAN  3
#define  HS_MON_CHAN 4
#define  SP_MON_CHAN 5
#define  HB_MON_CHAN 6

#define  V_MON_CONV  0b00001011
#define  T_MON_CONV  0b00001111
#define  HS_MON_CONV 0b00010011
#define  SP_MON_CONV 0b00010111
#define  HB_MON_CONV 0b00011011

#define	LB_IN_PIN	1
#define	HB_IN_PIN	0
#define	SEL_IN_PIN	3	

#define  DIR_PIN     PORTA.5
#define  CLK_PIN     PORTB.6

#pragma origin 0x8
interrupt highPriorityServer( void )
{
   isr_pwm2();
   #pragma fastMode
}

// char tick;

#pragma origin 0x18
interrupt lowPriorityServer( void )
{
   uns16 sv_FSR0;
   int_save_registers
   sv_FSR0 = FSR0;

   if( TMR0IF )	isr_irda();
   if( TXIF )		isr_putchar();
   if( RCIF )		isr_getchar();
	if( TMR1IF )	isr_time();
	if( EEIF )		isr_ee_write();

   FSR0 = sv_FSR0;
   int_restore_registers
}


/*
Temp  Voltage	Count (4.096 ref)	Count (8 bit)
-40   4.99		3FF					FF
-20   4.96		3FF					FF
  0   4.87		3FF					FF
 25	4.55		3FF					FF
 50	3.82		3BA					EE
 75	2.74		2AC					AB
100	1.69		1A6					69
125	0.96		0EF					3B
*/

//unsigned long analogData[5];
const char analogConv[5] = { V_MON_CONV, T_MON_CONV, HS_MON_CONV, SP_MON_CONV, HB_MON_CONV }; 

void configAtoD( void )
{
   // Channels 2,3,4,5,6 are analog

   // set the ANS bits HI for AtoD inputs, LO for digital
   ANSEL = 0b01111100;
   
   // set TRIS bits HI for AtoD inputs
   TRISA.2 = 1;   // AN2 = RA2
   TRISA.4 = 1;   // AN3 = RA4
   TRISC.0 = 1;   // AN4 = RC0
   TRISC.1 = 1;   // AN5 = RC1
   TRISC.2 = 1;   // AN6 = RC2

   // set voltage ref to FVR - GND
   ADCON1 = 0b00001000;

   // enable FVR x 4
   // Therefore 4mV / count
   VREFCON0 = 0b10110000;
   
   // Left justified, acq = 8Tad, Fosc = Tosc/8 = 2uS
   ADCON2 = 0b00100001;
}

#include "time.c"
//#include "rtc.c"
#include "pwm2.c"
#include "io.c"
#include "debounce.c"
#include "eeprom.c"
#include "epwm.c"
//#include "param.c"
#include "irda.c"

//unsigned long voltage, temperature;
unsigned long voltage;
unsigned long hs_current, sp_current, hb_current;
static char channel;

#if 0
void int_atod( void )
{
   switch( channel )
   {
      case( 0 ):
         voltage = ( (unsigned long)ADRESH<<8 ) | ADRESL;
         ADCON0 = T_MON_CONV;
         channel = 1;
         break;

      case( 1 ):
         temperature = ( (unsigned long)ADRESH<<8 ) | ADRESL;
         ADCON0 = HS_MON_CONV;
         channel = 2;
         break;

      case( 2 ):
         hs_current = ( (unsigned long)ADRESH<<8 ) | ADRESL;
         ADCON0 = SP_MON_CONV;
         channel = 3;
         break;

      case( 3 ):
         sp_current = ( (unsigned long)ADRESH<<8 ) | ADRESL;
         ADCON0 = HB_MON_CONV;
         channel = 4;
         break;

      case( 4 ):
         hb_current = ( (unsigned long)ADRESH<<8 ) | ADRESL;

      default:
         ADCON0 = V_MON_CONV;
         channel = 0;
   }
}
#endif

#if 0
void int_atod_2( void )
{
	unsigned long oldVal, newVal, accum;

	oldVal = analogData[channel];
	newVal = ( (unsigned long)ADRESH<<8 ) | ADRESL;

	oldVal >>= 1;
	accum = oldVal;

	oldVal >>= 1;
	accum += oldVal;

	oldVal >>= 1;
	accum += oldVal;

	newVal >>= 3;
	accum += newVal;

   analogData[channel] = accum;

	channel++;
	if( channel > 4 )
		channel = 0;

	ADCON0 = analogConv[channel];
}   
#endif

uns8  temperature;

void getTemp( void )
{
   // Filter: NewTmp = (OldTemp + NewTemp )/2
	temperature += ADRESH;					// Carry flag is set maintaining a 9 bit result
	temperature = rr( temperature );		// rotate right through carry;
   ADCON0 = T_MON_CONV;						// Initiate next conversion
}

// Temperature compensation
// Note: Lower temperatures have higher acquired values
// Lo		Hi 	Ratio
//	<85	85		100%
// 85		90		90%
//	90		95		75%
//	95		>95	50%

#define TEMP_MINIMUM		143		// equal to 85'C
#define TEMP_MIDDLE		117		// equal to 95'C
#define TEMP_MAXIMUM		94			// equal to 105'C

enum { TEMP_COOL, TEMP_WARM, TEMP_HOT, TEMP_EXTREME, TEMP_MAX };

char tempRange;

char updateTempRange( void );
char setTempRange( char temperature );

char updateTempRange( void )
{
	char oldTempRange = tempRange;
	tempRange = setTempRange( temperature );
	return( oldTempRange ^ tempRange );
}

char setTempRange( char temperature )
{
	if( temperature > TEMP_MINIMUM )	return TEMP_COOL;
	if( temperature > TEMP_MIDDLE )	return TEMP_WARM;
	if( temperature > TEMP_MAXIMUM )	return TEMP_HOT;
	return TEMP_EXTREME;
}

char filteredInputs, oldInputs;

// lbIn is HI when Low Beam should be on
// hbIn is HI when High Beam should be on
// selIn is HI when Regular Low Beam should be on, LO when 18W Low Beam should be on

// LED Currents
//
// NOMINAL: 1.25V Reference
// HotSpot = 606mA   (0.33R current set resistor)
// Spread  = 606mA   (0.33R current set resistor)
// HiBeam  = 606mA   (0.33R current set resistor)
//
// Maximum: 2.50 Reference
// All = 1212mA
//
// DRIVE
// Hardware will drive at nominal if micro control pins are high impedance
//   this is fail-safe (or failed!) mode
// Hardware also drives at nominal if reference voltage is at 1.25V, 
//   or modulated such that the average is 1.25V
// The maximum allowable is 200% over driven, or average input of 2.50V,
//   or modulated such that the average is 2.50V
// The hardware is set so the that modulation voltage is 5.0V.
//   Therefore 25% duty cycle is nominal voltage, 50% duty cycle is double nominal voltage.
// The minimum allowable is 0.2V, or about 4% modulation.
//   Therefore the minimum modulation is set at 5%.
// 0% duty cycle will shut the driver off.
//
// current = 606 * PWM/64     ->    PWM = current * 64 / 606

// Correction factor = 1.16
// Current     Current	Vref     DutyCycle	320	256-  320	256-    
// Req'd       Adjust				(from Xl)	cnt	inv	rem	inv
//	0mA			0			0			0%           
//	750mA			871		1.80     31.4%			101	155	219	37
//	825mA		   958		1.98     33.5%			107	149	213	43
//	850mA		   987		2.04     34.2%			110	146	210	46
//	1200mA	   1394		2.87     44.1%			141	115	179	77

#define CURR_PERIOD		320	// Note that any PWM2 times MUST be less than 256 counts!!

//#define EPWM_0_MA			0
//#define EPWM_750_MA     107
//#define EPWM_825_MA     124
//#define EPWM_850_MA     129
//#define EPWM_1000_MA    162
//#define EPWM_1100_MA    184
//#define EPWM_1200_MA    206

enum { EPWM_0, EPWM_750, EPWM_825, EPWM_850, EPWM_1000, EPWM_1100, EPWM_1200, EPWM_MAX };

// Current entries in the structure are in the following sequence:
// 0ma, 750ma, 825ma, 850ma, 1000ma, 1100ma, 1200ma
// Temperature index goes from coolest to warmest
const struct { char current[EPWM_MAX]; } currentTable[TEMP_MAX] =
{
	{ 0, 112, 129, 135, 169, 191, 214 },
	{ 0,  96, 111, 116, 146, 166, 187 },
	{ 0,  70,  83,  87, 112, 129, 146 },
	{ 0,  28,  36,  39,  56,  67,  79 }
};	

const struct { char high; char low; } pwm2Table[TEMP_MAX] =
{
	{ 121, 71 },
	{ 140, 52 },
	{ 169, 23 },
	{ 236, 116}		// This one has a 160 count period as opposed to 320
};

//#define PWM2_750_MA     149
//#define PWM2_825_MA     132
//#define PWM2_850_MA     127

//#define REST_750_MA		43
//#define REST_825_MA		60
//#define REST_850_MA		65

#define OFF_PATTERN     0
#define LB_LP_PATTERN   1
#define HB_LP_PATTERN   2
#define LB_HP_PATTERN   3
#define HB_HP_PATTERN   4

#define MAX_PATTERN     5

// Pulse steering mode values
#define PSM_NONE  0b00000000  // All off
#define PSM_HS    0b00010100  // HotSpot under EPWM control
#define PSM_SP    0b00010010  // Spread under EPWM control
#define PSM_LB    0b00010110  // HotSpot and Spread under EPWM control
#define PSM_HB    0b00010001
#define PSM_HS_HB 0b00010101
#define PSM_SP_HB 0b00010011
#define PSM_ALL   0b00010111


char patternSelect;

void setPattern( void )
{
	char high, low;
   switch( filteredInputs & 0b00001011 )
   {
      case 0b00000010:
         patternSelect = LB_LP_PATTERN;
         high = pwm2Table[tempRange].high;
         low = pwm2Table[tempRange].low;
         pwm2_set( high, low );
         pwm2_start();
			epwm_set( currentTable[tempRange].current[EPWM_750], PSM_SP );
         break;

      case 0b00000001:
      case 0b00000011:
         patternSelect = HB_LP_PATTERN;
         pwm2_stop();
			epwm_set( currentTable[tempRange].current[EPWM_825], PSM_ALL );
         break;

      case 0b00001010:
         patternSelect = LB_HP_PATTERN;
         pwm2_stop();
			epwm_set( currentTable[tempRange].current[EPWM_1200], PSM_LB );
         break;

      case 0b00001001:
      case 0b00001011:
         patternSelect = HB_HP_PATTERN;
         pwm2_stop();
 			epwm_set( currentTable[tempRange].current[EPWM_1200], PSM_ALL );
        break;

      default:
         patternSelect = OFF_PATTERN;
         pwm2_stop();
			epwm_set( currentTable[tempRange].current[EPWM_0], PSM_NONE );
   }
}

void configTimer0( void )
{
   // T0CON
   // 0b WWWW WWWW
   //    |||| |+++-- T0PS: Prescaler
   //    |||| +----- PSA: 1=NO prescaler, 0=Prescaler
   //    |||+------- T0SE: 1=Hi to Lo transition, 0=Lo to Hi
   //    ||+-------- T0CS: 1=Count on Pin, 0=Count on internal
   //    |+--------- T08BIT: 1=8 bit mode, 0=16 bit mode
   //    +---------- TMR0ON: 1=Enabled, 0=Disabled
   //
   // Prescaler
   // 000   1:2
   // 001   1:4
   // 010   1:8
   // 011   1:16
   // 100   1:32
   // 101   1:64
   // 110   1:128
   // 111   1:256

	// T0 runs at 250KHz, so rolls over every 1.024mS 
#if FOSC == 4000000
#define T0CON_INIT	0b11010011	// Timer ON, 1:16, rolls ov
#elif FOSC == 16000000
#define T0CON_INIT	0b11010101	// Timer ON, 1:64
#else
#error main.c: There is no value for T0CON at the specified clock speed
#endif 
   // Freq = 16MHz, Fosc = 4MHz, Tosc = 250nS
	// T0 runs at 250KHz, so rolls over every 1.024mS 
   T0CON = T0CON_INIT;
   TMR0IP = 0;    // low priority
   
}


bit needToUpdate;

void main( void )
{
   // configuration
   // setup HI speed internal osc as defined in CONFIG1H
   OSCCON = 0b11110000;

   configAtoD();
	time_config();
   io_config();
   pwm2_config();
   epwm_config();
   irda_config();

   PORTA = 0;
   TRISA = 0b00011111;
   LATA = 0;
   WPUA = 0;
   IOCA = 0;
   DIR_PIN = 1;   // Set serial direction to Transmit

   PORTB = 0;
   TRISB = 0b10001111;
   LATB = 0;
   WPUB = 0;
   IOCB = 0;

   PORTC = 0;
   TRISC = 0b11111111;
   LATC = 0;

   // initialization
   initDebounce();

   IPEN = 1;   // Enable prioritized interrupts
   TMR0IE = 1; // enable IrDA timer interrupts
   TMR1IE = 1; // enable OS / RTC imterrupts
   TMR2IE = 0; // diables EPWM interrupts, not needed
   TMR3IE = 0; // disable PWM2 timer interrupts until needed

   GIEH = 1;   // Enable high priority interrupts, in this case TMR3 = PWM2
   GIEL = 1;   // Enable low priority interrupts

//   debug();

// WDT should be updated every iteration of the main loop.
// Main loop is 8mS
// WDT is set to 1:8, 32mS
// Enable WDT
   WDTCON = 1;

   PORTC = 0;
   TRISC = 0b11000111;

   filteredInputs = debounce( PORTA );
   oldInputs = filteredInputs;
   patternSelect = OFF_PATTERN;
   setPattern();

	needToUpdate = 0;

//	initTimers();	// load timers from EEPROM

   while( 1 )
   {
      clrwdt();
      if( tick )
      {
         tick = 0;
         filteredInputs = debounce( PORTA ); 
      
         if( oldInputs != filteredInputs )
         {
            oldInputs = filteredInputs; 
	         needToUpdate = 1;
         }

			if( updateTempRange() )
				needToUpdate = 1;

			if( needToUpdate )
			{
            setPattern();
            needToUpdate = 0;
			}

         getTemp();
         putchar( PORTA );
         putchar( filteredInputs );
         putchar( patternSelect );
			putchar( temperature );

      }
   }
}
